package org.goj.user.controller;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.goj.common.constant.CommonConstant;
import org.goj.common.constant.RedisConstant;
import org.goj.common.constant.ResultCode;
import org.goj.common.entity.auth.LoginUser;
import org.goj.common.entity.auth.Oauth2TokenDTO;
import org.goj.common.entity.base.Email;
import org.goj.common.entity.user.Role;
import org.goj.common.entity.user.User;
import org.goj.common.model.Result;
import org.goj.common.utils.BingImageUtil;
import org.goj.feign.feign.AuthFeignClient;
import org.goj.feign.feign.BaseFeignClient;
import org.goj.user.mapper.RoleMapper;
import org.goj.user.service.UserService;
import org.goj.user.vo.PersonCardVO;
import org.goj.user.vo.PersonClassifyVO;
import org.goj.user.vo.PersonInfo;
import org.goj.user.vo.PersonRecord;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 用户信息 前端控制器
 * </p>
 *
 * @author CK
 * @since 2020-12-02
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private RedisTemplate<Object, Object> redisTemplate;

    @Autowired
    private BaseFeignClient baseFeignClient;

    @Resource
    private BCryptPasswordEncoder passwordEncoder;

    @Resource
    private UserService userService;

    @Resource
    private RoleMapper roleMapper;

    @Autowired
    private AuthFeignClient authFeignClient;

    /**
     * 用户登录
     *
     * @param email    邮箱
     * @param password 密码
     * @return Result<Oauth2TokenDTO>
     * @author CK
     * @date 2020/12/17 11:36
     */
    @PostMapping("/login")
    Result<Oauth2TokenDTO> loginToGetToken(@RequestParam(value = "email") String email,
                                           @RequestParam(value = "password") String password) {
        Map<String, String> parameters = Map.of("grant_type", "password", "client_id", "GojPassword", "client_secret", "GojSecretPassword", "username", email, "password", password);
        User user = new LoginUser();
        user.setUsername("GojPassword");
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user, null);
        Result<Oauth2TokenDTO> result = authFeignClient.postAccessToken(token, parameters);
        if (!result.getCode().equals(ResultCode.SUCCESS.getCode())) {
            return result;
        }
        return Result.succeed("登录成功！", result.getData());
    }

    /**
     * 用户注册
     *
     * @param email      邮箱
     * @param password   密码
     * @param rePassword 重复密码
     * @param captcha    验证码
     * @return Result<Oauth2TokenDTO>
     * @author CK
     * @date 2020/12/17 11:36
     */
    @PostMapping("/register")
    Result<Oauth2TokenDTO> registerToGetToken(@RequestParam(value = "email") String email,
                                              @RequestParam(value = "password") String password,
                                              @RequestParam(value = "rePassword") String rePassword,
                                              @RequestParam(value = "captcha") String captcha) {
        Assert.isTrue(password.equals(rePassword) && StrUtil.isNotBlank(password), "两次输入密码不相等！");
        String redisCode = (String) redisTemplate.opsForValue().get(RedisConstant.EMAIL_VALIDATE_CODE + email);
        Assert.notNull(redisCode, "验证码不存在或已过期！");
        Assert.isTrue(redisCode.equals(captcha), "验证码输入错误！");
        Assert.isTrue(validUser(email).size() == 0, "此邮箱已经注册！");
        boolean f = userService.insertUserAndUserRole(email, password);
        Assert.isTrue(f, "注册失败！");
        Result<Oauth2TokenDTO> oauth2TokenResult = loginToGetToken(email, password);
        redisTemplate.delete(RedisConstant.EMAIL_VALIDATE_CODE + email);
        return Result.succeed("注册成功,自动登录！", oauth2TokenResult.getData());
    }

    /**
     * 根据邮箱获取用户信息
     *
     * @param email 邮箱
     * @return Result<LoginUser>
     * @author CK
     * @date 2021/3/14 8:50
     */
    @GetMapping(value = "/getUserByEmail")
    public Result<LoginUser> findByLoginUser(@RequestParam(value = "email") String email) {
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq(StrUtil.isNotBlank(email), "email", email);
        List<User> users = userService.list(userQueryWrapper);
        Assert.isTrue(users.size() < 2, "警告，查询用户不唯一！");
        Assert.isTrue(users.size() > 0, "用户名或密码错误");
        LoginUser loginUser = new LoginUser();
        BeanUtils.copyProperties(users.get(0), loginUser);
        List<Role> roles = roleMapper.getRoleByUser(users.get(0).getUserId());
        Assert.isTrue(roles.size() > 0, "该用户尚未分配角色！");
        loginUser.setRoles(roles);
        return Result.succeed("查询用户成功！", loginUser);
    }

    public List<User> validUser(String email) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("email", email);
        return userService.list(queryWrapper);
    }

    /**
     * 发送验证码给用户邮箱
     *
     * @param to 用户邮箱
     * @return Result<String>
     * @author CK
     * @date 2020/12/15 20:29
     */
    @PostMapping("/userEmail")
    public Result<String> sendSimpleEmail(@RequestParam(value = "to") String to) {
        if (!StringUtils.isEmpty(redisTemplate.opsForValue().get(RedisConstant.EMAIL_VALIDATE_CODE + to))) {
            return Result.succeed(ResultCode.ERROR_VERIFICATION_CODE_K0003.getCode(), ResultCode.ERROR_VERIFICATION_CODE_K0003.getMessage());
        }
        String code = RandomUtil.randomString(CommonConstant.BASE_STRING, 6);
        Email email = new Email();
        email.setTo(to);
        email.setSubject("GOJ邮箱验证码");
        email.setContent("您的验证码为：" + code + "，请于2分钟内使用。如非本人操作，请忽略本邮件。");
        Result<String> result = baseFeignClient.sendSimpleEmail(email);
        Assert.isTrue(result.getCode().equals(ResultCode.SUCCESS.getCode()), result.getMessage());
        redisTemplate.opsForValue().set(RedisConstant.EMAIL_VALIDATE_CODE + email.getTo(), code, 120, TimeUnit.SECONDS);
        return Result.succeed("验证码发送成功！");
    }

    /**
     * 用户找回密码
     *
     * @param email      邮箱
     * @param password   密码
     * @param rePassword 重复密码
     * @param captcha    验证码
     * @return Result<Boolean>
     * @author CK
     * @date 2021/4/3 11:42
     */
    @PostMapping("/findPassword")
    public Result<Boolean> finPassword(@RequestParam(value = "email") String email,
                                       @RequestParam(value = "password") String password,
                                       @RequestParam(value = "rePassword") String rePassword,
                                       @RequestParam(value = "captcha") String captcha) {
        Assert.isTrue(password.equals(rePassword) && StrUtil.isNotBlank(password), "两次输入密码不相等！");
        String redisCode = (String) redisTemplate.opsForValue().get(RedisConstant.EMAIL_VALIDATE_CODE + email);
        Assert.notNull(redisCode, "验证码不存在或已过期！");
        Assert.isTrue(redisCode.equals(captcha), "验证码输入错误！");
        Assert.isTrue(validUser(email).size() == 1, "此邮箱账号尚未注册！");
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("email", email);
        User user = new User();
        user.setPassword(passwordEncoder.encode(password));
        boolean flag = userService.update(user, queryWrapper);
        Assert.isTrue(flag, "密码更新失败！");
        redisTemplate.delete(RedisConstant.EMAIL_VALIDATE_CODE + email);
        return Result.succeed("找回密码成功，快去登录吧！", true);
    }

    /**
     * 获取个人卡片信息
     *
     * @return Result<String>
     * @author CK
     * @date 2021/4/5 15:31
     */
    @GetMapping("/personCard")
    public Result<PersonCardVO> personCard(@RequestParam(value = "userId") Integer userId) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("nick", "head_portrait", "submit", "solved", "description", "defunct").eq("user_id", userId);
        User user = userService.getOne(queryWrapper);
        if (user == null) {
            return Result.failed(ResultCode.USER_ERROR_A0201.getCode(), ResultCode.USER_ERROR_A0201.getMessage());
        }
        if (!"N".equals(user.getDefunct())) {
            return Result.failed(ResultCode.USER_ERROR_A0202.getCode(), ResultCode.USER_ERROR_A0202.getMessage());
        }
        PersonCardVO personCardVO = new PersonCardVO();
        BeanUtils.copyProperties(user, personCardVO);
        personCardVO.setBingImage(BingImageUtil.getUrl());
        return Result.succeed("获取个人卡片信息成功", personCardVO);
    }

    /**
     * 查询个人擅长分类
     *
     * @param userId 用户ID
     * @return Result<Map < List < String>,Map<Integer,String>>>
     * @author CK
     * @date 2021/4/6 12:44
     */
    @GetMapping("/personClassify")
    public Result<PersonClassifyVO> personClassify(@RequestParam(value = "userId") Integer userId) {
        PersonClassifyVO personClassify = userService.personClassify(userId);
        return Result.succeed("查询个人擅长分类成功", personClassify);
    }

    /**
     * 查询个人活动记录
     *
     * @param userId  用户ID
     * @param size    分页数量
     * @param current 当前页
     * @return Result<PersonRecord>
     * @author CK
     * @date 2021/4/6 14:58
     */
    @GetMapping("/personRecord")
    public Result<Page<PersonRecord>> personRecord(
            @RequestParam(value = "userId") Integer userId,
            @RequestParam(value = "current", required = false, defaultValue = "1") Integer current,
            @RequestParam(value = "size", required = false, defaultValue = "10") Integer size) {
        Page<PersonRecord> personRecord = userService.personRecord(userId, current, size);
        return Result.succeed("查询个人活动记录成功", personRecord);
    }

    /**
     * 查询个人信息
     *
     * @param userId 用户ID
     * @return Result<PersonInfo>
     * @author CK
     * @date 2021/4/7 11:19
     */
    @GetMapping("/personInfo")
    public Result<PersonInfo> personInfo(@RequestParam(value = "userId") Integer userId) {
        PersonInfo personInfo = userService.personInfo(userId);
        return Result.succeed("查询个人信息成功", personInfo);
    }

    /**
     * 更新个人基本信息
     *
     * @param userId      用户ID
     * @param sex         性别
     * @param nick        昵称
     * @param description 签名
     * @return Result<Boolean>
     * @author CK
     * @date 2021/4/7 16:07
     */
    @PostMapping("/baseInfo")
    public Result<Boolean> baseInfo(@RequestParam(value = "userId") Integer userId,
                                    @RequestParam(value = "sex") Integer sex,
                                    @RequestParam(value = "nick") String nick,
                                    @RequestParam(value = "description", required = false) String description) {
        boolean flag = userService.baseInfo(userId, sex, nick, description);
        return Result.succeed("更新基本信息成功", flag);
    }

    /**
     * 更新用户头像
     *
     * @param image 图片
     * @return Result<String>
     * @author CK
     * @date 2021/4/7 16:48
     */
    @PostMapping("/uploadImage")
    public Result<String> uploadImage(@RequestParam(value = "userId") Integer userId,
                                      @RequestPart(value = "image") MultipartFile image) {
        String url = userService.uploadImage(baseFeignClient, userId, image);
        return Result.succeed(ResultCode.SUCCESS.getCode(), "更新头像成功", url);
    }

    /**
     * 更新用户的学校信息
     *
     * @param userId 用户ID
     * @param school 学校名称
     * @param clbum  班级
     * @param number 学号
     * @return Result<Boolean>
     * @author CK
     * @date 2021/4/7 19:16
     */
    @PostMapping("/schoolInfo")
    public Result<Boolean> schoolInfo(@RequestParam(value = "userId") Integer userId,
                                      @RequestParam(value = "school", required = false) String school,
                                      @RequestParam(value = "clbum", required = false) String clbum,
                                      @RequestParam(value = "number", required = false) String number) {
        boolean flag = userService.schoolInfo(userId, school, clbum, number);
        return Result.succeed("更新学校信息成功", flag);
    }

    /**
     * 更新用户邮箱
     *
     * @param userId   用户ID
     * @param password 密码
     * @param email    邮箱
     * @param code     验证码
     * @return Result<Boolean>
     * @author CK
     * @date 2021/4/7 20:11
     */
    @PostMapping("/updateEmail")
    public Result<Boolean> updateEmail(@RequestParam(value = "userId") Integer userId,
                                       @RequestParam(value = "password") String password,
                                       @RequestParam(value = "email") String email,
                                       @RequestParam(value = "code") String code) {
        boolean flag = userService.updateEmail(userId, password, email, code);
        return Result.succeed("更新邮箱成功,请重新登录", flag);
    }

    /**
     * 用户修改密码
     *
     * @param userId   用户ID
     * @param password 原密码
     * @param newPass  新密码
     * @param rePass   确认密码
     * @param email    邮箱
     * @param code     验证码
     * @return Result<Boolean>
     * @author CK
     * @date 2021/4/7 21:06
     */
    @PostMapping("/updatePassword")
    public Result<Boolean> updatePassword(@RequestParam(value = "userId") Integer userId,
                                          @RequestParam(value = "password") String password,
                                          @RequestParam(value = "newPass") String newPass,
                                          @RequestParam(value = "rePass") String rePass,
                                          @RequestParam(value = "email") String email,
                                          @RequestParam(value = "code") String code) {
        boolean flag = userService.updatePassword(userId, password, newPass, rePass, email, code);
        return Result.succeed("修改密码成功,请重新登录", flag);
    }

    /**
     * 查询用户列表
     *
     * @param name      昵称或班级或学号
     * @param defunct   状态
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @param current   当前页
     * @param size      页面大小
     * @return Result<Page < User>>
     * @author CK
     * @date 2021/5/10 21:01
     */
    @GetMapping("/userList")
    public Result<Page<User>> userList(
            @RequestParam(value = "name", required = false) String name,
            @RequestParam(value = "defunct", required = false) String defunct,
            @RequestParam(value = "startDate", required = false) String startDate,
            @RequestParam(value = "endDate", required = false) String endDate,
            @RequestParam(value = "current", required = false, defaultValue = "1") Integer current,
            @RequestParam(value = "size", required = false, defaultValue = "10") Integer size) {
        Page<User> page = userService.userList(name, defunct, startDate, endDate, current, size);
        return Result.succeed("查询用户列表成功", page);
    }

    /**
     * 添加用户
     *
     * @param email    邮箱
     * @param password 密码
     * @param defunct  状态
     * @param nick     昵称
     * @param sex      性别
     * @param roleId   角色ID
     * @param clbum    班级
     * @param number   学号
     * @return Result<Boolean>
     * @author CK
     * @date 2021/5/11 14:45
     */
    @PostMapping("insertUser")
    public Result<Boolean> insertUser(
            @RequestParam(value = "email") String email,
            @RequestParam(value = "password") String password,
            @RequestParam(value = "defunct") String defunct,
            @RequestParam(value = "nick", required = false) String nick,
            @RequestParam(value = "sex") Integer sex,
            @RequestParam(value = "roleId") Integer roleId,
            @RequestParam(value = "clbum", required = false) String clbum,
            @RequestParam(value = "number", required = false) String number) {
        boolean flag = userService.insertUser(email, password, defunct, nick, sex, roleId, clbum, number);
        Assert.isTrue(flag, "添加用户失败");
        return Result.succeed("添加用户成功", true);
    }

    /**
     * 更新用户状态
     *
     * @param userId  用户ID
     * @param defunct 状态
     * @return Result<Boolean>
     * @author CK
     * @date 2021/5/11 14:59
     */
    @PostMapping("/updateUserDefunct")
    public Result<Boolean> updateUserDefunct(
            @RequestParam(value = "userId") Integer userId,
            @RequestParam(value = "defunct") String defunct) {
        User user = new User();
        user.setUserId(userId);
        user.setDefunct("Y".equals(defunct) ? "N" : "Y");
        boolean b = userService.updateById(user);
        Assert.isTrue(b, "更新状态失败");
        return Result.succeed("更新状态成功", true);
    }

}
